package be.dataflow.xtime;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.SingleClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.json.JSONException;
import org.json.JSONObject;

import android.util.Log;

public class DFHttpClient {
	public static final boolean DEV = false;
	public static final String LOGGER_TAG = "xtime";
	private static final int HTTP_STATUS_OK = 200;
	private static byte[] sBuffer = new byte[512];
	private static String useragent = "adf android-http-client 0.1";
	private static final int timeoutConnection = 5000;
	private static final int timeoutSocket = 5000;
	
	// URLS
	public static final String testurl = "http://copywaste.org/status.html";

	/**
	 * Get a page and request that it be of a JSON-contenttype
	 * @param url
	 * @return JSONObject A JSON-Object or NULL
	 * @throws ApiException
	 */
	protected static JSONObject getJSON(String url) throws ApiException {
		// Setting up basic communication
		HttpParams httpParameters = new BasicHttpParams();

		// Set the timeout in milliseconds until a connection is established.
		HttpConnectionParams.setConnectionTimeout(httpParameters,timeoutConnection);

		// Set the default socket timeout (SO_TIMEOUT)
		// in milliseconds which is the timeout for waiting for data.
		HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);

		// Loading client with timeout-params
		HttpClient client = new DefaultHttpClient(httpParameters);

		HttpGet getter = new HttpGet(url);
		getter.addHeader("User-Agent", useragent);
		getter.addHeader("Accept", "application/json");
		getter.addHeader("Content-type", "application/json");

		try {
			HttpResponse response = client.execute(getter);

			// Check if server response is valid
			StatusLine status = response.getStatusLine();
			if (status.getStatusCode() != HTTP_STATUS_OK) {
				throw new ApiException("Invalid response from server: "+ status.toString());
			}

			// Pull content stream from response
			HttpEntity entity = response.getEntity();
			InputStream inputStream = entity.getContent();

			ByteArrayOutputStream content = new ByteArrayOutputStream();

			// Read response into a buffered stream
			int readBytes = 0;
			while ((readBytes = inputStream.read(sBuffer)) != -1) {
				content.write(sBuffer, 0, readBytes);
			}

			// Return result from buffered stream
			return new JSONObject(content.toString());
		} catch (IOException e) {
			throw new ApiException("Problem communicating with API ("+ e.getMessage() + ")", e);
		} catch (JSONException e) {
			throw new ApiException("We did not receive valid JSON ("+ e.getMessage() + ")", e);
		}
	}
	
	/**
	 * Get the content of an url and return it as a string
	 * @param url
	 * @return String The content from the response
	 * @throws ApiException
	 */
	protected static String get(String url) throws ApiException {
		// Setting up basic communication
		HttpParams httpParameters = new BasicHttpParams();

		// Set the timeout in milliseconds until a connection is established.
		HttpConnectionParams.setConnectionTimeout(httpParameters,timeoutConnection);

		// Set the default socket timeout (SO_TIMEOUT)
		// in milliseconds which is the timeout for waiting for data.
		HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);

		// Loading client with timeout-params
		HttpClient client = new DefaultHttpClient(httpParameters);

		HttpGet getter = new HttpGet(url);
		getter.addHeader("User-Agent", useragent);

		try {
			HttpResponse response = client.execute(getter);

			// Check if server response is valid
			StatusLine status = response.getStatusLine();
			if (status.getStatusCode() != HTTP_STATUS_OK) {
				throw new ApiException("Invalid response from server: "+ status.toString());
			}

			// Pull content stream from response
			HttpEntity entity = response.getEntity();
			InputStream inputStream = entity.getContent();

			ByteArrayOutputStream content = new ByteArrayOutputStream();

			// Read response into a buffered stream
			int readBytes = 0;
			while ((readBytes = inputStream.read(sBuffer)) != -1) {
				content.write(sBuffer, 0, readBytes);
			}

			// Return result from buffered stream
			return new String(content.toString());
		} catch (IOException e) {
			throw new ApiException("Problem communicating with API ("+ e.getMessage() + ")", e);
		} 
	}
	
	protected static String post(String url, JSONObject json) throws ApiException {
		// Setting up basic communication
		HttpParams httpParameters = new BasicHttpParams();

		// Set the timeout in milliseconds until a connection is established.
		HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);

		// Set the default socket timeout (SO_TIMEOUT)
		// in milliseconds which is the timeout for waiting for data.
		HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);

		// Loading client with timeout-params
		HttpClient client = new DefaultHttpClient(httpParameters);

		HttpPost getter = new HttpPost(url);
		getter.addHeader("User-Agent", useragent);
		getter.addHeader("Accept", "application/json");
		getter.addHeader("Content-type", "application/json");
		
		try {
			getter.setEntity(new StringEntity(json.toString(),"UTF-8"));
			HttpResponse response = client.execute(getter);

			// Check if server response is valid
			StatusLine status = response.getStatusLine();
			if (status.getStatusCode() != HTTP_STATUS_OK) {
				throw new ApiException("Invalid response from server: "+ status.toString());
			}

			// Pull content stream from response
			HttpEntity entity = response.getEntity();
			InputStream inputStream = entity.getContent();

			ByteArrayOutputStream content = new ByteArrayOutputStream();

			// Read response into a buffered stream
			int readBytes = 0;
			while ((readBytes = inputStream.read(sBuffer)) != -1) {
				content.write(sBuffer, 0, readBytes);
			}

			// Return result from buffered stream
			return new String(content.toByteArray());
		} catch (IOException e) {
			throw new ApiException("Problem communicating with API ("+ e.getMessage() + ")", e);
		}
	}

	/**
	 * We are checking if we can reach the testurl
	 * 
	 * @return boolean Returns true when our testurl is readable
	 */
	public static boolean webIsReachable() {
		String httpreturn = "";
		try {
			httpreturn = get(testurl).trim();
			logger("testurl:" + httpreturn + ".");

			return httpreturn.equals("on");
		} catch (Exception e) {
			logger(e);
		}

		return false;
	}

	public static void logger(String message) {
		if (DEV) {
			Log.w(LOGGER_TAG, message);
		}
	}

	public static void logger(Throwable e) {
		if (DEV) {
			if (e != null) {
				String msg = e.getMessage();
				if (msg == null) {
					msg = "Exception.Message was Null";
				}
				Log.e(LOGGER_TAG, msg);

				for (StackTraceElement ste : e.getStackTrace()) {
					Log.e(LOGGER_TAG, ste.getClassName() + " - "
							+ ste.getMethodName() + " [ " + ste.getFileName()
							+ " : " + ste.getLineNumber() + " ]");
				}
			} else {
				Log.e(LOGGER_TAG, "Exception was Null");
			}
		}
	}

	/**
	 * Thrown when there were problems contacting the remote API server, either
	 * because of a network error, or the server returned a bad status code.
	 */
	@SuppressWarnings("serial")
	public static class ApiException extends Exception {
		public ApiException(String detailMessage, Throwable throwable) {
			super(detailMessage, throwable);
		}

		public ApiException(String detailMessage) {
			super(detailMessage);
		}
	}
}
